﻿using Lingmeng.EntityFrameworkCore.Core.Base;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Video.Domain;

namespace Lingmeng.EntityFrameworkCore.core
{
    public class UnitOfWork<IDbContext> : IUnitOfWork where IDbContext:MasterDbContext<IDbContext>,IDisposable
    {
        public bool IsDisposed { get; private set; }
        /// <summary>
        /// 是否完成
        /// </summary>
        public bool IsComleted { get; private set; }
        private readonly IDbContext _dbContext;
        public UnitOfWork(IDbContext dbContext)
        {
            _dbContext = dbContext;
        }
        public async Task BeginTransactionAsync(CancellationToken cancellationToken = default)
        {
            IsDisposed = false;
            await _dbContext.Database.BeginTransactionAsync(cancellationToken);
        }

        public async Task CommitTransactionAsync(CancellationToken cancellationToken = default)
        {
            if(IsComleted)
            {
                return;
            }
            IsComleted = true;
            ApplyChangeConventions();
            try
            {
                //提交事务
                await _dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
                await _dbContext.Database.CommitTransactionAsync(cancellationToken).ConfigureAwait(false);
            }
            catch
            {
                //发生异常回滚事务
                await RollBackTransactionAsync(cancellationToken).ConfigureAwait(false);
            }
        }

        public async Task RollBackTransactionAsync(CancellationToken cancellationToken = default)
        {
            await _dbContext.Database.RollbackTransactionAsync(cancellationToken).ConfigureAwait(false);
        }

        public async Task<int> SaveChangeAsync(CancellationToken cancellationToken = default)
        {
            ApplyChangeConventions();
            return await _dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
        }
        private void ApplyChangeConventions()
        {
            _dbContext.ChangeTracker.DetectChanges();
            foreach (EntityEntry entity in _dbContext.ChangeTracker.Entries())
            {
                switch (entity.State)
                {
                    case EntityState.Added:
                        UnitOfWork<IDbContext>.SetCreation(entity);
                        break;
                }
            }
        }

        private static void SetCreation(EntityEntry entity)
        {
            if (entity.Entity is Entity creator)
            {
                creator.CreatedTime = DateTime.Now;
            }
        }
    }
}
